/****************************************************************

kernel/gdtidtisr.c
GDT, IDT and ISR setup/configuration

Copyright (c) 2006 Andrew Wilcox. All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:

"This product includes software developed by Andrew Wilcox for
AWOS. (http://code.google.com/p/awos)."

Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments normally
appear.

4. The names "AWOS" and "Andrew Wilcox" must not be used to
endorse or promote products derived from this software without
prior written permission. For written permission, please contact
Andrew.R.Wilcox@Gmail.com.

5. Products derived from this software may not be called "AWOS"
 nor may "AWOS" appear in their name, without prior written
 permission of Andrew Wilcox.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDREW WILCOX BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

*****************************************************************/

#include <kernel.h>
#include <asm.h>
#include <string.h>

extern BOOL debug;

struct gdt_entry {
	unsigned short limit_low;
	unsigned short base_low;
	unsigned char base_middle;
	unsigned char access;
	unsigned char granularity;
	unsigned char base_high;
} __attribute__ ((packed));

struct gdt_ptr {
	unsigned short limit;
	unsigned int base;
} __attribute__ ((packed));

struct gdt_entry gdt[3];
struct gdt_ptr gp;

struct idt_entry {
	unsigned short base_low;
	unsigned short sel;
	unsigned char zero;
	unsigned char flags;
	unsigned short base_high;
} __attribute__ ((packed));

struct idt_ptr {
	unsigned short limit;
	unsigned int base;
} __attribute__ ((packed));

struct idt_entry idt[256];
struct idt_ptr idtp;

extern void gdt_flush();
extern void idt_load();

extern void isr00(); extern void isr01(); extern void isr02(); extern void isr03(); extern void isr04();
extern void isr05(); extern void isr06(); extern void isr07(); extern void isr08(); extern void isr09();
extern void isr0A(); extern void isr0B(); extern void isr0C(); extern void isr0D(); extern void isr0E();
extern void isr0F(); extern void isr10(); extern void isr11(); extern void isr12(); extern void isr13();
extern void isr14(); extern void isr15(); extern void isr16(); extern void isr17(); extern void isr18();
extern void isr19(); extern void isr1A(); extern void isr1B(); extern void isr1C(); extern void isr1D();
extern void isr1E(); extern void isr1F(); extern void isr20(); extern void isr21(); extern void isr22();
extern void isr23(); extern void isr24(); extern void isr25(); extern void isr26(); extern void isr27();
extern void isr28(); extern void isr29(); extern void isr2A(); extern void isr2B(); extern void isr2C();
extern void isr2D(); extern void isr2E(); extern void isr2F(); extern void isr30(); extern void isr31();
extern void isr32(); extern void isr33(); extern void isr34(); extern void isr35(); extern void isr36();
extern void isr37(); extern void isr38(); extern void isr39(); extern void isr3A(); extern void isr3B();
extern void isr3C(); extern void isr3D(); extern void isr3E(); extern void isr3F(); extern void irq00();
extern void irq01(); extern void irq02(); extern void irq03(); extern void irq04(); extern void irq05();
extern void irq06(); extern void irq07(); extern void irq08(); extern void irq09(); extern void irq0A();
extern void irq0B(); extern void irq0C(); extern void irq0D(); extern void irq0E(); extern void irq0F();

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
	gdt[num].base_low = (base & 0xFFFF);
	gdt[num].base_middle = (base >> 16) & 0xFF;
	gdt[num].base_high = (base >> 24) & 0xFF;
	
	gdt[num].limit_low = (limit & 0xFFFF);
	gdt[num].granularity = ((limit >> 16) & 0x0F);
	
	gdt[num].granularity |= (gran & 0xF0);
	gdt[num].access = access;
	
	return;
}

void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
	idt[num].base_low = (base & 0xFFFF);
	idt[num].base_high = (base >> 16) & 0xFFFF;
	idt[num].sel = sel;
	idt[num].zero = 0;
	idt[num].flags = flags;
	
	return;
}

void *irq_routines[16] = 
{
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0
};

void KiIrqInstall(int irq, void(*handler)(struct regs *r))
{
	irq_routines[irq] = handler;
	
	return;
}

void KiIrqUninstall(int irq)
{
	irq_routines[irq] = 0;
	
	return;
}

/****************************************************************
KiIrqHandler() - handles IRQ interrupts
Dispatches to proper handler function
Input: struct regs *r - registers
Output: Nothing
****************************************************************/
void KiIrqHandler(struct regs *r)
{
	void (*handler)(struct regs *r);
	
	handler = irq_routines[r->int_no];
	if(handler)
		handler(r);
	else
		if(r->int_no > 0x00) dprintf("KiIrqHandler: recieved unhandled IRQ number %02X\n", r->int_no);

	if(r->int_no >= 0x08)
		outportb(0xA0, 0x20);

	outportb(0x20, 0x20);
	
	return;
}

void KsGdtIdtIsr()
{
	gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
	gp.base = (int) &gdt;
	
	gdt_set_gate(0, 0, 0, 0, 0);
	gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
	gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
	
	gdt_flush();
	
	idtp.limit = (sizeof(struct idt_entry) * 0xFF) - 1;
	idtp.base = (int) &idt;
	
	memset((unsigned char *)&idt, 0, sizeof(struct idt_entry) * 256);
	
	//do not add ISRs above this line//
	idt_set_gate(0x00, (unsigned)isr00, 0x08, 0x8E);
	idt_set_gate(0x01, (unsigned)isr01, 0x08, 0x8E);
	idt_set_gate(0x02, (unsigned)isr02, 0x08, 0x8E);
	idt_set_gate(0x03, (unsigned)isr03, 0x08, 0x8E);
	idt_set_gate(0x04, (unsigned)isr04, 0x08, 0x8E);
	idt_set_gate(0x05, (unsigned)isr05, 0x08, 0x8E);
	idt_set_gate(0x06, (unsigned)isr06, 0x08, 0x8E);
	idt_set_gate(0x07, (unsigned)isr07, 0x08, 0x8E);
	idt_set_gate(0x08, (unsigned)isr08, 0x08, 0x8E);
	idt_set_gate(0x09, (unsigned)isr09, 0x08, 0x8E);
	idt_set_gate(0x0A, (unsigned)isr0A, 0x08, 0x8E);
	idt_set_gate(0x0B, (unsigned)isr0B, 0x08, 0x8E);
	idt_set_gate(0x0C, (unsigned)isr0C, 0x08, 0x8E);
	idt_set_gate(0x0D, (unsigned)isr0D, 0x08, 0x8E);
	idt_set_gate(0x0E, (unsigned)isr0E, 0x08, 0x8E);
	idt_set_gate(0x0F, (unsigned)isr0F, 0x08, 0x8E);
	idt_set_gate(0x10, (unsigned)isr10, 0x08, 0x8E);
	idt_set_gate(0x11, (unsigned)isr11, 0x08, 0x8E);
	idt_set_gate(0x12, (unsigned)isr12, 0x08, 0x8E);
	idt_set_gate(0x13, (unsigned)isr13, 0x08, 0x8E);
	idt_set_gate(0x14, (unsigned)isr14, 0x08, 0x8E);
	idt_set_gate(0x15, (unsigned)isr15, 0x08, 0x8E);
	idt_set_gate(0x16, (unsigned)isr16, 0x08, 0x8E);
	idt_set_gate(0x17, (unsigned)isr17, 0x08, 0x8E);
	idt_set_gate(0x18, (unsigned)isr18, 0x08, 0x8E);
	idt_set_gate(0x19, (unsigned)isr19, 0x08, 0x8E);
	idt_set_gate(0x1A, (unsigned)isr1A, 0x08, 0x8E);
	idt_set_gate(0x1B, (unsigned)isr1B, 0x08, 0x8E);
	idt_set_gate(0x1C, (unsigned)isr1C, 0x08, 0x8E);
	idt_set_gate(0x1D, (unsigned)isr1D, 0x08, 0x8E);
	idt_set_gate(0x1E, (unsigned)isr1E, 0x08, 0x8E);
	idt_set_gate(0x1F, (unsigned)isr1F, 0x08, 0x8E);
	idt_set_gate(0x20, (unsigned)isr20, 0x08, 0x8E);
	idt_set_gate(0x21, (unsigned)isr21, 0x08, 0x8E);
	idt_set_gate(0x22, (unsigned)isr22, 0x08, 0x8E);
	idt_set_gate(0x23, (unsigned)isr23, 0x08, 0x8E);
	idt_set_gate(0x24, (unsigned)isr24, 0x08, 0x8E);
	idt_set_gate(0x25, (unsigned)isr25, 0x08, 0x8E);
	idt_set_gate(0x26, (unsigned)isr26, 0x08, 0x8E);
	idt_set_gate(0x27, (unsigned)isr27, 0x08, 0x8E);
	idt_set_gate(0x28, (unsigned)isr28, 0x08, 0x8E);
	idt_set_gate(0x29, (unsigned)isr29, 0x08, 0x8E);
	idt_set_gate(0x2A, (unsigned)isr2A, 0x08, 0x8E);
	idt_set_gate(0x2B, (unsigned)isr2B, 0x08, 0x8E);
	idt_set_gate(0x2C, (unsigned)isr2C, 0x08, 0x8E);
	idt_set_gate(0x2D, (unsigned)isr2D, 0x08, 0x8E);
	idt_set_gate(0x2E, (unsigned)isr2E, 0x08, 0x8E);
	idt_set_gate(0x2F, (unsigned)isr2F, 0x08, 0x8E);
	idt_set_gate(0x30, (unsigned)isr30, 0x08, 0x8E);
	idt_set_gate(0x31, (unsigned)isr31, 0x08, 0x8E);
	idt_set_gate(0x32, (unsigned)isr32, 0x08, 0x8E);
	idt_set_gate(0x33, (unsigned)isr33, 0x08, 0x8E);
	idt_set_gate(0x34, (unsigned)isr34, 0x08, 0x8E);
	idt_set_gate(0x35, (unsigned)isr35, 0x08, 0x8E);
	idt_set_gate(0x36, (unsigned)isr36, 0x08, 0x8E);
	idt_set_gate(0x37, (unsigned)isr37, 0x08, 0x8E);
	idt_set_gate(0x38, (unsigned)isr38, 0x08, 0x8E);
	idt_set_gate(0x39, (unsigned)isr39, 0x08, 0x8E);
	idt_set_gate(0x3A, (unsigned)isr3A, 0x08, 0x8E);
	idt_set_gate(0x3B, (unsigned)isr3B, 0x08, 0x8E);
	idt_set_gate(0x3C, (unsigned)isr3C, 0x08, 0x8E);
	idt_set_gate(0x3D, (unsigned)isr3D, 0x08, 0x8E);
	idt_set_gate(0x3E, (unsigned)isr3E, 0x08, 0x8E);
	idt_set_gate(0x3F, (unsigned)isr3F, 0x08, 0x8E);
	idt_set_gate(0x40, (unsigned)irq00, 0x08, 0x8E);
	idt_set_gate(0x41, (unsigned)irq01, 0x08, 0x8E);
	idt_set_gate(0x42, (unsigned)irq02, 0x08, 0x8E);
	idt_set_gate(0x43, (unsigned)irq03, 0x08, 0x8E);
	idt_set_gate(0x44, (unsigned)irq04, 0x08, 0x8E);
	idt_set_gate(0x45, (unsigned)irq05, 0x08, 0x8E);
	idt_set_gate(0x46, (unsigned)irq06, 0x08, 0x8E);
	idt_set_gate(0x47, (unsigned)irq07, 0x08, 0x8E);
	idt_set_gate(0x48, (unsigned)irq08, 0x08, 0x8E);
	idt_set_gate(0x49, (unsigned)irq09, 0x08, 0x8E);
	idt_set_gate(0x4A, (unsigned)irq0A, 0x08, 0x8E);
	idt_set_gate(0x4B, (unsigned)irq0B, 0x08, 0x8E);
	idt_set_gate(0x4C, (unsigned)irq0C, 0x08, 0x8E);
	idt_set_gate(0x4D, (unsigned)irq0D, 0x08, 0x8E);
	idt_set_gate(0x4E, (unsigned)irq0E, 0x08, 0x8E);
	idt_set_gate(0x4F, (unsigned)irq0F, 0x08, 0x8E);
	//do not add ISRs below this line//
	
	idt_load();
	
	//remap IRQs to start at INT 33h
	outportb(0x20, 0x11);
	outportb(0xA0, 0x11);
	outportb(0x21, 0x40);
	outportb(0xA1, 0x48);
	outportb(0x21, 0x04);
	outportb(0xA1, 0x02);
	outportb(0x21, 0x01);
	outportb(0xA1, 0x01);
	outportb(0x21, 0x00);
	outportb(0xA1, 0x00);
	
	return;
}

/****************************************************************
irq_handler() - Function for legacy bootup code that uses old
				naming convention.  DO NOT USE FOR PROD CODE!
Input: struct regs *r - registers
Output: Nothing
****************************************************************/
void irq_handler(struct regs *r)
{
	KiIrqHandler(r);
	
	return;
}
